package com.blog.support.shiro;

import com.blog.sys.dao.SysUserRepository;
import com.blog.sys.entity.SysPermission;
import com.blog.sys.entity.SysRole;
import com.blog.sys.entity.SysUser;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 登录认证、权限认证实现
 */
public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    private SysUserRepository sysUserRepository;
    private final static Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);

    /**
     * Shiro 的权限授权是通过继承AuthorizingRealm抽象类，重载doGetAuthorizationInfo();当访问到页面的时候，链接配置了相应的权限或者 Shiro 标签才会执行此方法否则不会执行，所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现，直接返回 null 即可。在这个方法中主要是使用类：SimpleAuthorizationInfo进行角色的添加和权限的添加。
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SysUser userInfo  = (SysUser)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        for(SysRole role:userInfo.getRoleList()){
            authorizationInfo.addRole(role.getRole());
            for(SysPermission p:role.getPermissions()){
                authorizationInfo.addStringPermission(p.getPermission());
            }
        }
        return authorizationInfo;
    }


    /**
     * 登录认证实现
     *
     * <p>
     *     在认证、授权内部实现机制中都有提到，最终处理都将交给Real进行处理。因为在 Shiro 中，最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。通常情况下，在 Realm 中会直接从我们的数据源中获取 Shiro 需要的验证信息。可以说，Realm 是专用于安全框架的 DAO. Shiro 的认证过程最终会交由 Realm 执行，这时会调用 Realm 的getAuthenticationInfo(token)方法。
     *
     *     该方法主要执行以下操作:
     *
     * <li> 1、检查提交的进行认证的令牌信息
     * <li> 2、根据令牌信息从数据源(通常为数据库)中获取用户信息
     * <li> 3、对用户信息进行匹配验证。
     * <li> 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
     * <li> 5、验证失败则抛出AuthenticationException异常信息。
     * </p>
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        SimpleAuthenticationInfo authenticationInfo = doAuthentication(token);
        if (authenticationInfo == null) return null;
        return authenticationInfo;
    }

    private SimpleAuthenticationInfo doAuthentication(AuthenticationToken token) {
        logger.info("权限认证中...");
        String username = (String) token.getPrincipal();
        logger.info("凭证：{}" , token.getCredentials());
        SysUser userInfo = sysUserRepository.findByUsername(username);
//        logger.info("----->>用户信息 = {}", JSON.toJSONString((userInfo)));
        logger.info("");
        if(userInfo == null){
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用户名
                userInfo.getPassword(), //密码
                ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }
}
